/*
 * Copyright 2021 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.kie.mojos;

import java.io.File;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.BiConsumer;

import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.kie.model.ConfigSet;
import org.kie.model.ProjectDefinition;
import org.kie.model.ProjectStructure;
import org.kie.utils.GeneratedProjectUtils;

/**
 * Abstract Mojo defining common parameters shared across implementing Mojos.
 */
public abstract class AbstractMojoDefiningParameters extends AbstractMojo {

    @Parameter(defaultValue = "${session}", readonly = true)
    protected MavenSession mavenSession;

    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    protected MavenProject project;

    /**
     * Defining properties for the project to be generated including resources to be copied into it.
     * 
     * <pre>
     *  &lt;projectDefinitions&gt;
     *    &lt;projectDefinition&gt;
     *      &lt;id&gt;&lt;/id&gt;
     *      &lt;groupId&gt;&lt;/groupId&gt;
     *      &lt;artifactId&gt;&lt;/artifactId&gt;
     *      &lt;packageName&gt;&lt;/packageName&gt;
     *      &lt;copyResources&gt;
     *        &lt;resource&gt;
     *          &lt;includes&gt;&lt;/includes&gt;
     *          &lt;excludes&gt;&lt;/excludes&gt;
     *        &lt;/resource&gt;
     *      &lt;/copyResources&gt;
     *      &lt;copySources&gt;
     *        &lt;package&gt;
     *          &lt;name&gt;org.kie.sth&lt;/name&gt;
     *          &lt;type&gt;main&lt;/type&gt;
     *          &lt;language&gt;java&lt;/language&gt;
     *        &lt;/package&gt;
     *      &lt;/copySources&gt;
     *    &lt;/projectDefinition&gt;
     *    &lt;projectDefinition&gt;
     *             ...
     *    &lt;/projectDefinition&gt;
     *  &lt;/projectDefinitions&gt;
     * </pre>
     */
    @Parameter(required = true)
    protected List<ProjectDefinition> projectDefinitions;

    /**
     * Defining structure of the project to be generated by specifying archetype and possible cleanup of generated resources.
     * 
     * <pre>
     * &lt;projectStructures&gt;
     *   &lt;projectStructure&gt;
     * 	   &lt;id&gt;quarkus&lt;/id&gt;
     * 	   &lt;archetype&gt;
     * 	     &lt;type&gt;ARCHETYPE&lt;/type&gt;
     * 	     &lt;archetype&gt;
     * 	       &lt;groupId&gt;org.kie.kogito&lt;/groupId&gt;
     * 	       &lt;artifactId&gt;kogito-quarkus-archetype&lt;/artifactId&gt;
     * 	       &lt;version&gt;7.11.0.redhat-210224&lt;/version&gt;
     * 	     &lt;/archetype&gt;
     * 	   &lt;/generate&gt;
     *     &lt;commonConfig&gt;
     * 	     &lt;deleteResources&gt;
     * 	       &lt;resource&gt;
     * 	         &lt;directory&gt;src/main/resources&lt;/directory&gt;
     *           &lt;includes&gt;
     *             &lt;include&gt;*&lt;/include&gt;
     *             &lt;include&gt;**&#47;*&lt;/include&gt;
     *           &lt;/includes&gt;
     *           &lt;excludes&gt;
     *             &lt;exclude&gt;application.properties&lt;/exclude&gt;
     *           &lt;/excludes&gt;
     *         &lt;/resource&gt;
     *       &lt;/deleteResources&gt;
     *       &lt;copySources&gt;
     *         &lt;package&gt;
     *           &lt;name&gt;org.kie.sth&lt;/name&gt;
     *           &lt;type&gt;test&lt;/type&gt;
     *           &lt;language&gt;java&lt;/language&gt;
     *         &lt;/package&gt;
     *       &lt;/copySources&gt;
     *       &lt;dependencies&gt;
     *         &lt;dependency&gt;
     *         ...
     *         &lt;/dependency&gt;
     *       &lt;/dependencies&gt;
     *     &lt;/commonConfig&gt;
     *     &lt;configSets&gt;
     *       &lt;configSet&gt;
     *         &lt;id&gt;id-to-be-activated-by&lt;/id&gt;
     *         &lt;copySources&gt;
     *           &lt;package&gt;
     *             &lt;name&gt;org.kie.sth&lt;/name&gt;
     *             &lt;type&gt;test&lt;/type&gt;
     *             &lt;language&gt;java&lt;/language&gt;
     *           &lt;/package&gt;
     *         &lt;/copySources&gt;
     *         &lt;dependencies&gt;
     *           &lt;dependency&gt;
     *           ...
     *           &lt;/dependency&gt;
     *         &lt;/dependencies&gt;
     *       &lt;/configSet&gt;
     *     &lt;/configSets&gt;
     * 	 &lt;/projectStructure&gt;
     * &lt;/projectStructures&gt;
     * </pre>
     */
    @Parameter(required = true)
    protected List<ProjectStructure> projectStructures;

    /**
     * Defining reusable configuration sets {@linkplain ConfigSet} which can be then referenced from a ConfigSet using
     * {@linkplain ConfigSet#getReusableConfig()}.
     *
     * <pre>
     * &lt;reusableConfigSets&gt;
     *   &lt;configSet&gt;
     *     &lt;id&gt;clean-main-resources-and-readme&lt;/id&gt;
     *     ...
     *   &lt;/configSet&gt;
     *   &lt;configSet&gt;
     *     &lt;id&gt;kogito-scesim&lt;/id&gt;
     *     ...
     *   &lt;/configSet&gt;
     * &lt;/reusableConfigSets&gt;
     * </pre>
     */
    @Parameter
    protected List<ConfigSet> reusableConfigSets;

    /**
     * List parameter allowing to limit the combinations to just defined projectStructures.
     * 
     * <pre>
     * &lt;activeStructures&gt;
     *   &lt;activeStructure&gt;
     *     name-of-the-structure-defined-already
     *   &lt;/activeStructure&gt;
     *   &lt;activeStructure&gt;
     *     a-different-id
     *   &lt;/activeStructure&gt;
     * &lt;/activeStructures&gt;
     * </pre>
     */
    @Parameter(property = "active.structures")
    protected Set<String> activeStructures = new HashSet<>();

    /**
     * List parameter allowing to limit the combinations to just defined projectDefinition.
     * 
     * <pre>
     * &lt;activeDefinitions&gt;
     *   &lt;activeDefinition&gt;
     *     name-of-the-definition-defined-already
     *   &lt;/activeDefinition&gt;
     *   &lt;activeDefinition&gt;
     *     a-different-id
     *   &lt;/activeDefinition&gt;
     * &lt;/activeDefinition&gt;
     * </pre>
     */
    @Parameter(property = "active.definitions")
    protected Set<String> activeDefinitions = new HashSet<>();

    /**
     * List parameter to configure which pre-defined configSets are supposed to be executed as part of execution.
     * 
     * <pre>
     * &lt;activeConfigSets&gt;
     *   &lt;activeConfigSet&gt;
     *     name-of-the-configSet-defined-already
     *   &lt;/activeConfigSet&gt;
     *   &lt;activeConfigSet&gt;
     *     a-different-id
     *   &lt;/activeConfigSet&gt;
     * &lt;/activeConfigSets&gt;
     * </pre>
     */
    @Parameter(property = "active.config.sets")
    protected Set<String> activeConfigSets = new HashSet();

    /**
     * File denoting location where generated projects will be placed.
     * <p/>
     * For getting generated project location use
     * {@linkplain GeneratedProjectUtils#getOutputDirectoryForGeneratedProject(Path, ProjectDefinition, ProjectStructure)}
     */
    @Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true)
    protected File outputDirectory;

    private ActiveMojoSetup activeMojoSetup;

    /**
     * Provides a way to access easily active pairs of {@linkplain ProjectDefinition}:{@linkplain ProjectStructure}.
     *
     * @return {@linkplain ActiveMojoSetup} instance allowing to perform action defined by a {@linkplain BiConsumer} instance.
     */
    public ActiveMojoSetup getActiveMojoSetup() {
        if (activeMojoSetup == null) {
            activeMojoSetup = new ActiveMojoSetup()
                    .setProjectDefinitions(projectDefinitions)
                    .setActiveDefinitions(activeDefinitions)
                    .setProjectStructures(projectStructures)
                    .setActiveStructures(activeStructures)
                    .setActiveConfigSets(activeConfigSets)
                    .setReusableConfigSets(reusableConfigSets);
        }
        return activeMojoSetup;
    }
}
